Flutter Overlay
介绍
Overlay 是一个 Stack 的 widget,可以将 overlay entry 插入到 overlay 中,使独立的 child 窗口悬浮于其他 widget 之上。
因为 Overlay 本身使用的是 Stack 布局,所以 overlay entry 可以使用 Positioned 或者 AnimatedPositioned 在 overlay 中定位自己的位置。
当我们创建 MaterialApp 的时候,它会自动创建一个 Navigator,然后创建一个 Overlay; 然后利用这个 Navigator 来管理路由中的界面。
基础使用
引入 Overlay 组件
最小的工程代码如下:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HomePage(),
));
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Overlay();
}
}
此时会展示一个黑屏,但是 OverLay 已经引入了。
其中,MaterialApp 组件不可省略,要不然会报错 textDirection != null': is not true.
这说明 MaterialApp 中会初始化一些显示的基本配置。
initialEntries
这个属性用于初始化 Overlayout 默认展示元素。
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Overlay(
initialEntries: [
OverlayEntry(builder: (context) => Text("Hello"))
],
);
}
}
此时会在屏幕左上角出现一个不带主题的 Text。
向 Overlay 中插入元素
继续拓展上面的示例,将文本改为可点击,点击后向 Overlay 中插入一个新的 OverlayEntry:
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Overlay(
initialEntries: [
OverlayEntry(builder: (context) {
return GestureDetector(
child: Container(
width: 200,
height: 50,
child: Text("弹出居中元素"),
),
onTap: () {
Overlay.of(context).insert(OverlayEntry(builder: (context) {
return Center(child: Text("居中弹层"));
}));
},
);
})
],
);
}
}
效果如图:
删除元素
再添加一个按钮,点击删除刚刚插入的弹层。有两种方法,一个是调用 Overlay 的 remove 方法,传入 OverlayEntry 示例。另一个方法是直接调用 OverlayEntry 实例的 remvoe 方法。
具体代码示例如下:
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Overlay(
initialEntries: [
OverlayEntry(builder: (context) {
return Scaffold(
appBar: AppBar(title: Text("首页")),
body: MaterialButton(
child: Text("弹出居中元素"),
onPressed: () {
OverlayEntry entry;
entry = OverlayEntry(builder: (context) {
return Center(
child: Material(
child: Container(
color: Colors.pink,
width: 200,
height: 200,
child: Column(children: [
Text("居中弹层"),
MaterialButton(
child: Text('关闭'),
onPressed: () => entry.remove())
]
))));
}
);
Overlay.of(context).insert(entry);
},
),
);
})
],
);
}
}
OverlayEntry 的 opaque 属性
如果 entry 不透明,overlay 为了提高效率,会不再构建下面的 entry,除非它们设置了 maintainState 属性。
在上面的示例中,在创建 OverlayEntry 时多传入一个 opaque 为 true。
此时中央弹层弹出时会展示如下效果: 无|缩略图
这是因为我们将 OverlayEntry 声明为不透明(其实 View 只占了中心部分空间),但是对于 Overlay 来说,只要 opaque,它就不再渲染底层组件,这中央中央弹层弹出时首页便消失了。点击关闭后首页重新出现。
主要就是两个方法,往 Overlay 中插入 entry,删除 Overlay 中的 entry。
//创建OverlayEntry
Overlay entry = new OverlayEntry(builder:(){/*在这里创建对应的widget*/});
//往Overlay中插入插入OverlayEntry
Overlay.of(context).insert(overlayEntry);
//调用entry自身的remove()方法,从所在的overlay中移除自己
entry.remove();
OverlayState
这是 Overlay 实现中的状态。
状态
名称 | 类型 | 说明 |
---|---|---|
_entries | List<OverlayEntry> | 视图栈 |
OverlayEntry
Overlay 中的元素,包含一个 WidgetBuilder。通过 OverlayState.insert 方式进行插入操作。使用 Overlay.of 方法,获取到 BuildContext 中最近的 Overlay。一个 OverlayEntry 一次只能加入一个 Overlay。OverlayEntry 有一个 remove 方法,调用后会将自己从 Overlay 中删除。
由于 Overlay 使用 Stack 布局,OverlayEntry 可以使用 Positioned 和 AnimatedPositioned,实现在 Overlay 中定位。
构造
传入三个参数:
名称 | 类型 | 说明 | 备注 |
---|---|---|---|
builder | WidgetBuilder | 视图构造器 | 调用 markNeedsBuild 可触发重复构建 |
_opaque | bool | 是否不透明 | 如果 entry 不透明,overlay 为了提高效率,会不再构建下面的 entry
除非它们设置了 maintainState 属性 |
maintainState | bool | 保持状态 | 这个 entry 是否必须要位于视图树中,即使有个不透明的盖住它
默认有不透明的,下面的就不渲染了。 对于 StatefulWidget 来说,这会导致它不被实例化(不走) 设置之后,就算不展示也会渲染。 这个操作开销大,小心使用。 特别是如果一个组件,设置了这个属性,在底下一直 setState,会增大耗电 |
maintainState
这个属性在 Navigator 和 Route 中使用,作用是当页面位于后台时(应该指非栈顶)。
这样页面的 Futures 才能保证运行完成后返回。